import sys, time


# <Class: ProgressBar/>
class ProgressBar(object):
    """
    Some information about ProgressBar:
    - ...
    """
    # <Method: __init__/>
    def __init__(self, Title = ' ', Total = 0, Width = 25):
        super(ProgressBar, self).__init__()
        if Total <= 0:
            raise RuntimeError("Total must be > 0")
        # end-if
        self._title = Title
        self._count = 0
        self._total = Total
        self._width = Width
        self._postfix_str = ''
        self._num_per_block = int((Total + Width - 1) / Width)
        self._timestamps = [ ]
        self._progressstamps = [ ]
        self._remaining_h = -1
        self._remaining_m = -1
        self._remaining_s = -1
        self._current_write_width = 0
    # <Method: /__init__>

    # <Method: set_postfix_str/>
    def set_postfix_str(self, postfix_str):
        self._postfix_str = postfix_str
    # <Method: /set_postfix_str>

    # <Method: update/>
    def update(self, update_count = None):    
        if update_count is not None:
            self._count += update_count
        else:
            self._count += 1
        # end-if
        progress = min(1.0, self._count / self._total)
        progress_len = int(self._width * progress)
        progress_pecentage = int(round(progress * 10000)) / 100.0
        progress_blocknum = int(10 * (self._count % int(self._num_per_block)) / self._num_per_block)
        if self._count == self._total:
            progress_blocknum = ""
        # end-if
        self._progressstamps.append(progress)
        self._timestamps.append(time.time())
        if(len(self._timestamps) > 2):
            time_second = self._timestamps[len(self._timestamps) - 1] - self._timestamps[0]
            progress_delta = self._progressstamps[len(self._progressstamps) - 1] - self._progressstamps[0]
            remaining_time = time_second / progress_delta * (1.0 - progress)
            self._remaining_h = int(remaining_time) // 3600
            self._remaining_m = int(remaining_time - self._remaining_h * 3600) // 60
            self._remaining_s = int(remaining_time - self._remaining_h * 3600 - self._remaining_m * 60)
        # end-if
        sys.stdout.write( " " * self._current_write_width + "\r")
        sys.stdout.flush()
        current_write_content = """  {0}: [{1}] {2:.2f}% ({3}/{4}) | {5} | {6}:{7}:{8}\r""".format(
            self._title,
            '#'*progress_len + """{}""".format(progress_blocknum) + '-'*(self._width - progress_len - 1), 
            progress_pecentage,
            self._count, 
            self._total,
            self._postfix_str,
            "{}{}".format(self._remaining_h // 10, self._remaining_h % 10) if self._remaining_h >= 0 else "--",
            "{}{}".format(self._remaining_m // 10, self._remaining_m % 10) if self._remaining_m >= 0 else "--",
            "{}{}".format(self._remaining_s // 10, self._remaining_s % 10) if self._remaining_s >= 0 else "--",
            )
        sys.stdout.write( current_write_content )
        sys.stdout.flush()
    # <Method: /update>

    # <Method: close/>
    def close(self):
        if self._count >= self._total:
            sys.stdout.write('\r\n'); sys.stdout.flush()
        # end-if
    # <Method: /close>
# <Class: /ProgressBar>


if __name__ == "__main__":
    total = 50
    bar = ProgressBar(Title='Test', Total = total, Width = 100)
    for i in range(total):
        bar.set_postfix_str("""[{0}: {1:.3f}]""".format(i, i / 20.5))
        bar.update()
        time.sleep(0.5)
    bar.close()